/**
* @author Max Rupplin
* @date 5-18-08
*
* This class aids the related executable 'ddas_assimilator' with the tasks related to result assimilation.
*/

#include "DDAS_Assimilation_Handler.hpp"

#include "sched_msgs.h"
#include "sched_util.h"
#include "boinc_db.h"
#include "validate_util.h"
#include "parse.h"

#include <iostream>

using namespace std;

/**
* @author Max Rupplin
* @date 5-18-08
*
* This is the default constructor
*/
DDAS_Assimilation_Handler::DDAS_Assimilation_Handler(){}

/**
* @author Max Rupplin
* @date 5-18-08
*
* This is the default destructor
*/
DDAS_Assimilation_Handler::~DDAS_Assimilation_Handler(){}

/**
* @author Max Rupplin
* @date 5-18-08
* @param workunit_id The work unit unique ID
* @param canonical_result_id The canonical result unique ID
* @param job_id The job that the work unit and canonical result are associated with
* @return Returns 1 if successful, 0 otherwise
*
* 'insertMappingData' inserts its parameters into the science database to facilitate mapping between a job 
* and either or both its related work units and canonical results
*/
int DDAS_Assimilation_Handler::insertMappingData( int workunit_id, int canonical_result_id, int job_id )
{
        //database connection
        MYSQL* mysql = mysql_init(0);

        //attempt to connect
        mysql = mysql_real_connect(mysql, DBHOST, USERNAME, PASSWD, SCIENCE, 0, 0, 0);

        if( mysql==0 )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertMappingData : Unable to connect to '%s@%s'.\n",
                        SCIENCE,
                        DBHOST
                );

		//return error
                return 0;
        }

	//for the query
	char buffer[1024];

	//create query
	sprintf( buffer, "insert into canonical_result_mappings values (%u, %u, %u)", workunit_id, canonical_result_id, job_id );

	//execute and check for error
	if( mysql_query( mysql, buffer ) )
	{
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertMappingData : The following SQL statement failed:\n%s\n",
                        buffer
                );

		//close connection
		mysql_close(mysql);

		//return error
		return 0;
	}
	
	//close connection
	mysql_close( mysql );

	//return success
	return 1;	
}


/**
* @author Max Rupplin
* @date 5-18-08
* @param data The DDAS_Client_Result_Data object containing the output data of the client's computation
* @param canonical_result The result we are concerned with
* @param job_id The job that this work belongs to
* @return Returns 1 is successful, 0 otherwise
*
* 'insertOutputFile' inserts the output file of the client's computation into the science database for possible future analysis
*/
int DDAS_Assimilation_Handler::insertOutputFile(DDAS_Client_Result_Data* data, const RESULT& canonical_result, int job_id)
{
	//holds the path to the canonical_result's output file
	string file_path;

	//database connection
	MYSQL* mysql = mysql_init(0);

	//attempt to connect
	mysql = mysql_real_connect(mysql, DBHOST, USERNAME, PASSWD, SCIENCE, 0, 0, 0);

	if( mysql==0 )
	{
        	log_messages.printf
                (
                	SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertOutputFile : Unable to connect to '%s@%s'.\n",
			SCIENCE,
			DBHOST
                );

		return 0;
	}

	//get path to file
	if( get_output_file_path( canonical_result, file_path ) )
	{
        	log_messages.printf
               	(
               		SCHED_MSG_LOG::MSG_CRITICAL,
                        "[RESULT#%d %s] check_set: can't get output filename\n",
                        canonical_result.id,
                        canonical_result.name
                );

                mysql_close(mysql);

		return 0;
	}

	char* output_file = NULL;

	//read file and return pointer to it
	read_file_malloc( file_path.c_str(), output_file );

	//some space for the insert statement
        char* result_data = new char[ strlen(output_file) + 1024];

	//mysql insertion strings
	sprintf( result_data, "insert into result_data values( %u, '%s' )", canonical_result.id, output_file );	

	//execute query
	if( mysql_query( mysql, result_data ) )
	{
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertOutputFile : The following SQL statement failed:\n%s\n",
                        result_data
                );
	
		//close connection
		mysql_close(mysql);
		
		//free memory
		delete[] result_data;

		//return error
		return 0;
	}

	//close connection
	mysql_close(mysql);

	//free memory
	if( output_file ) delete[] output_file;
	delete[] result_data;

	return 1;
}

/**
* @author Max Rupplin
* @date 5-18-08
* @param data The DDAS_Client_Result_Data object containing the output data of the client's computation
* @param canonical_result The result we are concerned with
* @param job_id The job that this work belongs to
* @return Returns 1 is successful, 0 otherwise
*
* 'insertDataRows' inserts the data contained inside of 'data' into the science database
*/
int DDAS_Assimilation_Handler::insertDataRows(DDAS_Client_Result_Data* data, const RESULT& canonical_result, int job_id)
{
        //holds the path to the canonical_result's output file
        string file_path;

        //database connection
        MYSQL* mysql = mysql_init(0);

        //attempt to connect
        mysql = mysql_real_connect(mysql, DBHOST, USERNAME, PASSWD, SCIENCE, 0, 0, 0);

	//failed to connect
	if( mysql==0 )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertDataRows : Unable to connect to '%s@%s'.\n",
			SCIENCE,
			DBHOST
                );

		//return error
                return 0;
        }

        //get path to file
        if( get_output_file_path( canonical_result, file_path ) )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "[RESULT#%d %s] check_set: can't get output filename\n",
                        canonical_result.id,
                        canonical_result.name
                );

		//close connection
                mysql_close(mysql);

		//return error
                return 0;
        }

	//for each data row in a result set
	for(unsigned int i=0; i<data->getRowCount(); i++)
	{
		//some space for the insert statement
        	char* data_rows = new char[1024];

		//mysql insert statement
		sprintf( data_rows, "insert into data_rows values( %u, '%s', '%s', %f )", canonical_result.id, data->getDefinitionNumber(i), data->getBinaryRepresentation(i), data->getSSE(i) );
	
		//execute insert statement
		if( mysql_query( mysql, data_rows ) )
		{
                	log_messages.printf
                	(
                	        SCHED_MSG_LOG::MSG_CRITICAL,
                	        "insertDataRows : The following SQL statement failed:\n%s\n",
                	        data_rows
	                );
		}

		//free memory
		delete[] data_rows;
	}

	//close connection
	mysql_close(mysql);

	//return success
	return 1;
}

/**
* @author Max Rupplin
* @date 5-18-08
* @param data The DDAS_Client_Result_Data object containing the output data of the client's computation
* @param canonical_result The result we are concerned with
* @param job_id The job that this work belongs to
* @return Returns 1 if successful, 0 otherwise
*
* 'insertCoefficients' inserts the regression coefficients contained in 'data' into the science database
*/
int DDAS_Assimilation_Handler::insertCoefficients(DDAS_Client_Result_Data* data, const RESULT& canonical_result, int job_id)
{
        //holds the path to the canonical_result's output file
        string file_path;

        //database connection
        MYSQL* mysql = mysql_init(0);

        //attempt to connect
        mysql = mysql_real_connect(mysql, DBHOST, USERNAME, PASSWD, SCIENCE, 0, 0, 0);
	
	if( mysql==0 )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "insertCoefficients : Unable to connect to '%s@%s'.\n",
			SCIENCE,
			DBHOST
                );

                return 0;
        }

        //get path to file
        if( get_output_file_path( canonical_result, file_path ) )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "[RESULT#%d %s] check_set: can't get output filename\n",
                        canonical_result.id,
                        canonical_result.name
                );

		//close connection
                mysql_close(mysql);

                return 0;
        }

	//for each data row in the result set
	for(unsigned int i=0; i<data->getRowCount() ; i++)
	{
		Coefficients** coeff_ptr;

		data->getCoefficients(&coeff_ptr);

		int coeff_count = coeff_ptr[i]->getSize();

		//for each set of coefficients in a data row
		for(unsigned int j=0; j<coeff_count; j++)
		{
			//some space for the SQL statement
			char* regression_coefficients = new char[1024];

			//insert statement to store the coefficients
			sprintf( regression_coefficients, "insert into regression_coefficients values ( %u, '%s', %u, %f )", canonical_result.id, data->getDefinitionNumber(i), i , coeff_ptr[i]->getCoefficient(j) );

			//execute insert
			if( mysql_query( mysql, regression_coefficients ) )
			{
				log_messages.printf
				(
					SCHED_MSG_LOG::MSG_CRITICAL,
					"insertCoefficients : The following SQL query failed:\n%s",
					regression_coefficients
				);
			}
			else
			{
                                log_messages.printf
                                (
                                        SCHED_MSG_LOG::MSG_DEBUG,
                                        "insertCoefficients : The following SQL query succeeded:\n%s\n",
                                        regression_coefficients
                                );
			}

			//free memory
			delete[] regression_coefficients;
		}
	}

	//close connection
	mysql_close(mysql);

	return 1;
}

/**
* @author Max Rupplin
* @date 5-18-08
* @param wu The work unit to be matched to a job
* @return Returns the associated job ID if found, 0 otherwise
*
* 'queryJobID' matches a work unit to a job 
*/
int DDAS_Assimilation_Handler::queryJobID(WORKUNIT& wu)
{
	MYSQL* mysql = mysql_init(0);

        mysql = mysql_real_connect(mysql, DBHOST, USERNAME, PASSWD, DDAS, 0, 0, 0);

	if( mysql==0 )
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_CRITICAL,
                        "queryJobID : Unable to connect to '%s@%s'.\n",
			DDAS,
			DBHOST
                );

                return 0;
        }
	
        //to hold the query
       	char buffer[512];

	//create query
        sprintf(buffer, "select job_id from ddas.workunit_to_job_mapping where wu_id = %u", wu.id);

	//execute and check for error
        if( mysql_query(mysql, buffer) )
	{
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_DEBUG,
                        "queryJobID : The following SQL query failed:\n%s\n",
                        buffer
                );
		
		//close connection
		mysql_close(mysql);

		//return error
		return 0;
	}

	//store the result
        MYSQL_RES* result = mysql_store_result(mysql);

	//did we get any rows in our result?
        if( mysql_num_rows(result) <= 0)
        {
                log_messages.printf
                (
                        SCHED_MSG_LOG::MSG_DEBUG,
                        "queryJobID : Unable to match WORKUNIT #%s to a job.\n",
                       	wu.id 
                );
	
		//free memory 
                mysql_free_result(result);

        	//close connection
		mysql_close(mysql);
	
		//return error
        	return 0;
        }

        //get first (only) row
        MYSQL_ROW row = mysql_fetch_row(result);

        //free result
        mysql_free_result(result);

        //get data from row
        int jobID = atoi(row[0]);

	//close connection
	mysql_close(mysql);

	//return success
	return jobID;
}


